﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;

using ERPStore.Validators;
using ERPStore.Extensions;
using ERPStore.Web.Routing;

using CloudSoft.Extensions;

namespace ERPStore.Offers.Services
{
	public class OffersService : ERPStore.Services.IOffersService
	{
		private static object m_lock = new object();

		public OffersService(AdminOffersService adminOffersService
			, ERPStore.Services.ISalesService salesService
			, ERPStore.Services.ICacheService cacheService
			, ERPStore.Services.ICatalogService catalogService)
		{
			this.AdminOffersService = adminOffersService;
			this.SalesService = salesService;
			this.CacheService = cacheService;
			this.CatalogService = catalogService;
		}

		internal AdminOffersService AdminOffersService { get; private set; }
		internal ERPStore.Services.ISalesService SalesService { get; private set; }
		internal ERPStore.Services.ICacheService CacheService { get; private set; }
		internal ERPStore.Services.ICatalogService CatalogService { get; private set; }

		#region IOffersService Members

		public ERPStore.Models.Offer GetOfferByLink(string link)
		{
			var offer = AdminOffersService.GetCurrentOffers().SingleOrDefault(i => i.Link.Equals(link, StringComparison.InvariantCultureIgnoreCase));

			return offer;	
		}

		public Models.OfferEx GetOfferById(int offerId)
		{
			var offer = AdminOffersService.GetCurrentOffers().SingleOrDefault(i => i.Id == offerId);
			return offer;
		}

		public void ApplyOffers(ERPStore.Models.Product product, ERPStore.Models.User user)
		{
			if (product == null)
			{
				return;
			}
			product.BestPrice = product.SalePrice;

			// Liste des offres pour un produit donné
			var offerList = AdminOffersService.GetCurrentOffers().Where(i => i.ProductList.Any(p => p.ProductId == product.Id));
			
			foreach (var offer in offerList)
			{
				ApplyOffer(offer, product, user);
			}

			// Application de la tarification client
			SalesService.ApplyBestPrice(product, user);
		}

		public void ApplyOffer(Models.OfferEx offer, ERPStore.Models.Product product, ERPStore.Models.User user)
		{
			// Recherche d'un tarif sur une offre
			var offeredProduct = offer.ProductList.FirstOrDefault(p => p.ProductId == product.Id);
			if (offeredProduct == null)
			{
				return;
			}

			// Ne pas appliquer l'offre s'il n'y a pas de stock alors qu'il est requis
			// Todo voir en temps reel
			if (offer.PhysicalStockMandatory
				&& !product.HasStock)
			{
				return;
			}

			// Ne pas appliquer d'offre sur un produit non vendable
			if (!product.IsSellable)
			{
				return;
			}

			if (offeredProduct.SalePrice.HasValue
				&& offeredProduct.SalePrice < product.BestPrice.Value)
			{
				product.BestPrice = new ERPStore.Models.Price(offeredProduct.SalePrice.Value, product.SalePrice.TaxRate);
				product.SelectedOffer = offer;
				if (offeredProduct.ShowStrikedPrice)
				{
					product.StrikedPrice = product.SalePrice;
				}

				if (offeredProduct.Discount.HasValue)
				{
					if (offeredProduct.Discount.HasValue)
					{
						product.Discount = Convert.ToDouble(offeredProduct.Discount);
					}
				}
			}

			// appliquer les frais de port fixe
			if (offeredProduct.FixedShippingPrice.HasValue)
			{
				product.Shipping.Strategy = ERPStore.Models.FeeTransportStrategy.FixedPrice;
				product.Shipping.FixedPrice = offeredProduct.FixedShippingPrice.Value;
			}

			product.OfferList.Add(offer);
			if (product.SelectedOffer == null)
			{
				product.SelectedOffer = offer;
			}

		}

		#endregion

		internal IList<ERPStore.Models.Product> GetProductList(Models.OfferEx offer, int pageId, int pageSize, string categoryCode, string brandName, out int count)
		{
			var key = string.Format("Offer:ProductList:{0}", offer.Id);
			var c = CacheService[key];
			var productList = c as IList<ERPStore.Models.Product>;
			if (productList == null)
			{
				lock (m_lock)
				{
					var productIdList = offer.ProductList
										.OrderBy(i => i.OrderIndex.GetValueOrDefault(int.MaxValue))
										.Select(i => new { ProductId = i.ProductId, OrderIndex = i.OrderIndex.GetValueOrDefault(int.MaxValue) })
										.Distinct();

					productList = CatalogService.GetProductListByIdList(productIdList.Select(i => i.ProductId), true);
					foreach (var product in productList)
					{
						var orderIndex = productIdList.Where(i => i.ProductId == product.Id).FirstOrDefault().OrderIndex;
						product.Tag = orderIndex;
					}
					SubstituteVariations(productList);
					// productList = CatalogService.ApplySort(productList);
					CacheService.Add(key, productList, DateTime.Now.AddHours(2));
				}
			}

			var pagedList = productList.OrderBy(i => (int)i.Tag).AsQueryable();

			if (!categoryCode.IsNullOrTrimmedEmpty())
			{
				pagedList = from product in pagedList
							let root = product.Category.GetRootProductCategory()
							where product.Category != null
									&& root != null
									&& root.Code == categoryCode
							select product;
			}

			if (!brandName.IsNullOrTrimmedEmpty())
			{
				pagedList = pagedList.Where(i => i.Brand != null && i.Brand.Name == brandName);
			}

			count = pagedList.Count();
			pagedList = pagedList.Skip(pageId * pageSize).Take(pageSize);

			return pagedList.ToList();
		}

		internal void SubstituteVariations(IList<ERPStore.Models.Product> result)
		{
			// Suppression des vides
			result.RemoveAll(i => i == null);
			// Recherche des variants
			var variantList = result.Where(i => i.VariationProductParentId.HasValue).ToList();
			// Suppression des variants dans la liste initiale
			result.RemoveAll(i => i.VariationProductParentId.HasValue);

			// On vide la liste pour trouver le variant par defaut
			while (true)
			{
				var variant = variantList.FirstOrDefault();
				if (variant == null)
				{
					break;
				}

				if (result.Any(i => i.Code == variant.Code
					|| i.VariationProductParentId == variant.VariationProductParentId))
				{
					variantList.Remove(variant);
					continue;
				}

				var defaultVariant = CatalogService.GetDefaultVariantByProductVariationId(variant.Id);
				defaultVariant = defaultVariant ?? variant;
				defaultVariant.Tag = variant.Tag;
				variantList.RemoveAll(i => i.VariationProductParentId.Value == variant.VariationProductParentId.Value);

				result.Insert(0, defaultVariant);
			}
		}


	}
}
